import doctest

def cmp_cls_name(cls1, cls2):
    return cmp(cls1.__name__, cls2.__name__)

class Amino(object):
    def __init__(self, left, right):
        '''
        Constructs a Amino with two operands.
        left - the left operand
        right - the right operand
        '''
        self.__left = left
        self.__right = right
        
    @staticmethod
    def get_predefined_aminos():
        '''
        >>> classes = Amino.get_predefined_aminos()
        >>> names = [cls.__name__ for cls in classes]
        >>> 'AddAmino' in names
        True
        >>> 'JumpAmino' in names
        True
        '''
        classes = [NullAmino] + UnaryAmino.__subclasses__() + BinaryAmino.__subclasses__()
        classes.sort(cmp_cls_name)
        return classes

    def getleft(self):
        return self.__left
    
    def getleftvalue(self, context):
        return self.__left.getvalue(context)
    
    def getright(self):
        return self.__right
    
    def getrightvalue(self, context):
        return self.__right.getvalue(context)

class EmptyAmino(Amino):
    pass

class NullAmino(EmptyAmino):
    def run(self, context):
        '''
        >>> NullAmino(None, None).run(None)
        '''
        pass
    
class UnaryAmino(Amino):
    def getoperand(self):
        return self.getright()
    
    def getoperandvalue(self, context):
        return self.getrightvalue(context)

class BinaryAmino(Amino):
    pass

class AssignAmino(BinaryAmino):
    def run(self, context):
        '''
        >>> from eden.evo.operands import *
        >>> from eden.evo.context import Context
        >>> context = Context()
        >>> left = RegisterOperand(1)
        >>> right = ConstOperand(11)
        >>> AssignAmino(left, right).run(context)
        >>> context.getregister(1)
        11
        '''
        self.getleft().setvalue(self.getrightvalue(context), context)
        
class AddAmino(BinaryAmino):
    def run(self, context):
        '''
        11 + 22 = 33
        >>> from eden.evo.operands import *
        >>> from eden.evo.context import Context
        >>> context = Context()
        >>> context.setregister(1, 11)
        >>> left = RegisterOperand(1)
        >>> right = ConstOperand(22)
        >>> AddAmino(left, right).run(context)
        >>> context.getregister(1)
        33
        '''
        left = self.getleft()
        right = self.getright()
        left.setvalue(left.getvalue(context) + right.getvalue(context), context)
    
class SubAmino(BinaryAmino):
    def run(self, context):
        '''
        33 - 22 = 11
        >>> from eden.evo.operands import *
        >>> from eden.evo.context import Context
        >>> context = Context()
        >>> context.setregister(1, 33)
        >>> left = RegisterOperand(1)
        >>> right = ConstOperand(22)
        >>> SubAmino(left, right).run(context)
        >>> context.getregister(1)
        11
        '''
        left = self.getleft()
        right = self.getright()
        left.setvalue(left.getvalue(context) - right.getvalue(context), context)
    
class MultiAmino(BinaryAmino):
    def run(self, context):
        '''
        11 * 2 = 22
        >>> from eden.evo.operands import *
        >>> from eden.evo.context import Context
        >>> context = Context()
        >>> context.setregister(1, 11)
        >>> left = RegisterOperand(1)
        >>> right = ConstOperand(2)
        >>> MultiAmino(left, right).run(context)
        >>> context.getregister(1)
        22
        '''
        left = self.getleft()
        right = self.getright()
        left.setvalue(left.getvalue(context) * right.getvalue(context), context)
    
class DivideAmino(BinaryAmino):
    def run(self, context):
        '''
        22 / 2 = 11
        >>> from eden.evo.operands import *
        >>> from eden.evo.context import Context
        >>> context = Context()
        >>> context.setregister(1, 22)
        >>> left = RegisterOperand(1)
        >>> DivideAmino(left, ConstOperand(2)).run(context)
        >>> context.getregister(1)
        11
        >>> DivideAmino(left, ConstOperand(0)).run(context)
        >>> context.getregister(1)
        0
        '''
        left = self.getleft()
        rightvalue = self.getrightvalue(context)
        result = 0 if rightvalue == 0 else left.getvalue(context) / rightvalue
        left.setvalue(result, context)
    
class ModAmino(BinaryAmino):
    def run(self, context):
        '''
        11 % 3 = 2
        >>> from eden.evo.operands import *
        >>> from eden.evo.context import Context
        >>> context = Context()
        >>> context.setregister(1, 11)
        >>> left = RegisterOperand(1)
        >>> ModAmino(left, ConstOperand(3)).run(context)
        >>> context.getregister(1)
        2
        >>> ModAmino(left, ConstOperand(0)).run(context)
        >>> context.getregister(1)
        0
        '''
        left = self.getleft()
        rightvalue = self.getrightvalue(context)
        result = 0 if rightvalue == 0 else left.getvalue(context) % rightvalue
        left.setvalue(result, context)
        
class CompareAmino(BinaryAmino):
    def run(self, context):
        '''
        >>> from eden.evo.operands import *
        >>> from eden.evo.context import Context
        >>> context = Context()
        >>> left = ConstOperand(1)
        >>> right = ConstOperand(2)
        >>> CompareAmino(left, right).run(context)
        >>> context.getcmpregister()
        -1
        '''
        leftvalue = self.getleftvalue(context)
        rightvalue = self.getrightvalue(context)
        if leftvalue == rightvalue:
            context.setcmpregister(0)
        elif leftvalue < rightvalue:
            context.setcmpregister(-1)
        else:
            context.setcmpregister(1)
            
class JumpAmino(UnaryAmino):
    def run(self, context):
        '''
        >>> from eden.evo.operands import *
        >>> from eden.evo.context import Context
        >>> context = Context()
        >>> right = ConstOperand(123)
        >>> JumpAmino(None, right).run(context)
        >>> context.getoffset()
        123
        '''
        offset = self.getoperandvalue(context)
        context.setoffset(offset)
        
class JumpEqAmino(UnaryAmino):
    def run(self, context):
        '''
        >>> from eden.evo.operands import *
        >>> from eden.evo.context import Context
        >>> right = ConstOperand(123)
        >>> context = Context()
        >>> amino = JumpEqAmino(None, right)
        
        >>> context.setoffset(0)
        >>> context.setcmpregister(0)
        >>> amino.run(context)
        >>> context.getoffset()
        123
        
        >>> context.setoffset(0)
        >>> context.setcmpregister(1)
        >>> amino.run(context)
        >>> context.getoffset()
        0
        '''
        if context.getcmpregister() == 0:
            context.setoffset(self.getoperandvalue(context))
     
class JumpNeAmino(UnaryAmino):
    def run(self, context):
        '''
        >>> from eden.evo.operands import *
        >>> from eden.evo.context import Context
        >>> right = ConstOperand(123)
        >>> context = Context()
        >>> amino = JumpNeAmino(None, right)
        
        >>> context.setoffset(0)
        >>> context.setcmpregister(0)
        >>> amino.run(context)
        >>> context.getoffset()
        0
        
        >>> context.setoffset(0)
        >>> context.setcmpregister(1)
        >>> amino.run(context)
        >>> context.getoffset()
        123
        '''
        if context.getcmpregister() != 0:
            context.setoffset(self.getoperandvalue(context))
     
class JumpGtAmino(UnaryAmino):
    def run(self, context):
        '''
        >>> from eden.evo.operands import *
        >>> from eden.evo.context import Context
        >>> right = ConstOperand(123)
        >>> context = Context()
        >>> amino = JumpGtAmino(None, right)
        
        >>> context.setoffset(0)
        >>> context.setcmpregister(1)
        >>> amino.run(context)
        >>> context.getoffset()
        123
        
        >>> context.setoffset(0)
        >>> context.setcmpregister(0)
        >>> amino.run(context)
        >>> context.getoffset()
        0
        '''
        if context.getcmpregister() > 0:
            context.setoffset(self.getoperandvalue(context))

class JumpLtAmino(UnaryAmino):
    def run(self, context):
        '''
        >>> from eden.evo.operands import *
        >>> from eden.evo.context import Context
        >>> right = ConstOperand(123)
        >>> context = Context()
        >>> amino = JumpLtAmino(None, right)

        >>> context.setoffset(0)
        >>> context.setcmpregister(-1)
        >>> amino.run(context)
        >>> context.getoffset()
        123
        
        >>> context.setoffset(0)
        >>> context.setcmpregister(0)
        >>> amino.run(context)
        >>> context.getoffset()
        0
        '''
        if context.getcmpregister() < 0:
            context.setoffset(self.getoperandvalue(context))

if __name__ == '__main__':
    doctest.testmod()